from cameralib import *
from time import time, sleep
from Tkinter import *
from ImageTk import PhotoImage
import sys
import Image
from time import time
import tkFont

class Application(Frame):

  def __init__(self):
  
    self.root = Tk()
    self.webcam = Webcam(resolution='medium')
    self.stream = self.webcam.stream(format='numpy')
    self.root.overrideredirect(1)
    self.width, self.height = self.root.winfo_screenwidth(), self.root.winfo_screenheight()
    # self.width, self.height = 800, 600
    self.root.geometry("%dx%d+0+0" % (self.width, self.height))
    self.root.focus_set()
    self.root.bind('<Key-Escape>', lambda e: self.root.quit())

    Frame.__init__(self, self.root, width=self.width, height=self.height, background="#000000")
    self.grid()
    self.rowconfigure(0, minsize=100)
    self.rowconfigure(1, minsize=self.height-100)
    
    # run it
    self.build()
    self.start()
    
  def build(self):
  
    self.command = StringVar()
    
    font = tkFont.Font(family="Georgia", size=26)
    commandline = Entry(self, font=font, 
      fg="#FFFFFF", bg="#000000", insertbackground="#A6A6A6",
      insertwidth=10,
      textvariable=self.command)
    commandline.grid(row=0, sticky=E+W)
    commandline.bind('<Return>', self.invoke)
    commandline.focus_force()
    
    canvas = Canvas(self, width=self.width, height=self.height-100, background="#000000", 
    highlightbackground="#000000")
    canvas.grid(row=1, sticky=N)
    self.canvas_width, self.canvas_height = self.width, self.height-100

    self.commandline = commandline
    self.canvas = canvas
    self.rearranging = False
    self.port_layout = dict()
    self.port = {
      "all" : Viewport(self, 0, 0, True),
      "red" : Viewport(self, 0, 1),
      "green" : Viewport(self, 0, 2),
      "blue"  : Viewport(self, 0, 3),
      "all*"  : Viewport(self, 1,0),
      "red*"  : Viewport(self, 1,1),
      "green*" : Viewport(self, 1,2),
      "blue*"  : Viewport(self, 1,3)}
    
    self.layout_ports()
    
  def start(self):
    self.camera_loop()
    self.root.mainloop()

  def invoke(self, ev):
    for line in self.command.get().split(";"):
      try:
        args = line.strip().split()
        command = args.pop(0)
        f = getattr(self, 'command_%s' % command)
        f(*args)
        self.command.set("")
      except Exception, e:
        print "Error: ", str(e)
    
  def command_test(self, *E, **F):
    print "testing testing: %s" % str(E)
  
  def command_exit(self, *E):
    sys.exit(0)
    
  def command_toggle(self, *targets):
    if not targets:
      targets = self.port.keys()
      
    for target in targets:
      p = self.port[target]
      p.visible = not p.visible
      if not p.visible:
        p.hide()
    self.layout_ports(animate=True)
    
  def command_show(self, *targets):
    if not targets:
      targets = self.port.keys()
    for target in targets:
      p = self.port[target]
      p.visible = True
    self.layout_ports(animate=True)

  def command_hide(self, *targets):
    if not targets:
      targets = self.port.keys()
    for target in targets:
      p = self.port[target]
      p.visible = False
      p.hide()
    self.layout_ports(animate=True)
    
  def camera_loop(self):
    im = self.stream.next()
    for x in ['all', 'red', 'green', 'blue']:
      filter = make_filter(x)
      self.port[x].show(im, filter)
      filter = make_filter(x, grayscale=True)
      self.port["%s*" % x].show(im, filter)
    self.canvas.update()
    self.after(100, self.camera_loop)
  
  def layout_ports(self, animate=False):
    # plan out the x layout
    J = 0
    for j in range(0, 4):
      col_visible = False
      for i in range(0, 2):
        p = self.port_layout[i,j]
        if p.visible:
          p.new_x = J
          col_visible = True
      if col_visible:
        J += 1

    delta_x = self.canvas_width / float(J)
    for i in range(0, 2):
      for j in range(0, 4):
        p = self.port_layout[i,j]
        if p.visible:
          p.new_x = int(p.new_x * delta_x)
          p.new_w = int(float(self.canvas_width) / J)
          
    # plan out the y layout
    I = 0
    for i in range(0, 2):
      row_visible = False
      for j in range(0, 4):
        p = self.port_layout[i,j]
        if p.visible:
          p.new_y = I
          row_visible = True
      if row_visible:
        I += 1
    delta_y = (self.canvas_height) / float(I)
    print "canvas height=%d, I=%d, dy=%.2f" % (self.canvas_height, I, delta_y)
    for (i,j) in [(x,y) for x in range(0, 2) for y in range(0, 4)]:
      p = self.port_layout[i,j]
      if p.visible:
        p.new_y = int(p.new_y * delta_y)
        p.new_h = int(float(self.canvas_height) / I)

    # move things:
    animate_info = list()
    T = 200
    steps = 20.
    for (i,j) in [(x,y) for x in range(0, 2) for y in range(0, 4)]:
      p = self.port_layout[i,j]
      if p.visible:
        p.keep_aspect()
        animate_info.append((p, p.arrange(animate=animate, T=T, steps=steps)))
    if animate:
      self.animate(steps, int(T/steps), animate_info)
  
  def animate(self, steps, t, animate_info):
    for (p, info) in animate_info:
      if info:
        p.animate(*info)
    if steps > 0:
      self.after(t, self.animate, steps-1, t, animate_info)
    
class Viewport:
  def __init__(self, app, i, j, visible=False):
    self.app = app
    self.i, self.j, self.visible = i, j, visible
    c = app.canvas
    self.app.port_layout[(i, j)] = self

    self.init()
    
  def init(self):
    self.id = None
    self.image = None
    self.x = self.app.width / 2
    self.y = self.app.height / 2
    self.size = int(320/1.2), int(240/1.2)
    
  def show(self, imarray, filter=None):
    if not self.visible:
      return
      
    if not filter==None:
      imarray = apply_filter(imarray, filter)

    self.image = PhotoImage(convert_numpy_pil(imarray).resize(self.size).transpose(Image.FLIP_LEFT_RIGHT))
    
    if not self.id:
      self.id = self.app.canvas.create_image(self.x, self.y, anchor=NW, image=self.image)
    else:
      self.app.canvas.itemconfigure(self.id, image=self.image)
  
  def keep_aspect(self):
    nw, nh = self.new_w, self.new_h
    if float(nw)/nh > 32./24:
      nw = nh/24. * 32
    else:
      nh = nw/32. * 24
    self.new_w, self.new_h = int(nw), int(nh)
    
  def arrange(self, animate=False, T=None, steps=None):
    x0, y0 = self.new_x, self.new_y
    if not animate or self.id == None:
      if not self.id == None:
        self.app.canvas.move(self.id, x0 - self.x, y0 - self.y)
      self.x, self.y = x0, y0
      self.size = self.new_w, self.new_h
    else:
      # compute the animation strategy:
      steps = float(steps)
      dx = int((self.new_x - self.x)/steps)
      dy = int((self.new_y - self.y)/steps)
      dw = int((self.new_w - self.size[0])/steps)
      dh = int((self.new_h - self.size[1])/steps)      
      return (dx, dy, dw, dh)

  def animate(self, dx, dy, dw, dh):
    self.app.canvas.move(self.id, dx, dy)
    self.size = self.size[0]+dw, self.size[1]+dh
    self.x += dx
    self.y += dy
    
  def hide(self):
    self.app.canvas.delete(self.id)
    self.visible = False
    self.init()
    
app = Application()
